package phone

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"mgs/global"
)

const (
	cmcc             byte = iota + 0x01 //中国移动
	cucc                                //中国联通
	ctcc                                //中国电信
	ctccV                               //电信虚拟运营商
	cuccV                               //联通虚拟运营商
	cmccV                               //移动虚拟运营商
	intLen           = 4
	charLen          = 1
	phoneIndexLength = 9
)

type PhoneRecord struct {
	Province string
	City     string
	ZipCode  string
	AreaZone string
	CardType string
}

var (
	content     []byte
	cardTypeMap = map[byte]string{
		cmcc:  "中国移动",
		cucc:  "中国联通",
		ctcc:  "中国电信",
		ctccV: "中国电信虚拟运营商",
		cuccV: "中国联通虚拟运营商",
		cmccV: "中国移动虚拟运营商",
	}
	total, firstOffset int32
)

func Init() {
	phoneDat := global.CONFIG.System.PhoneDat
	if len(phoneDat) > 0 {
		var err error
		content, err = ioutil.ReadFile(phoneDat)
		if err != nil {
			panic(err)
		}
		total = int32(len(content))
		firstOffset = get4(content[intLen : intLen*2])
	}
}

func (pr PhoneRecord) String() string {
	return fmt.Sprintf("AreaZone: %s\nCardType: %s\nCity: %s\nZipCode: %s\nProvince: %s\n", pr.AreaZone, pr.CardType, pr.City, pr.ZipCode, pr.Province)
}

func get4(b []byte) int32 {
	if len(b) < 4 {
		return 0
	}
	return int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
}

func getN(s string) (uint32, error) {
	var n, cutoff, maxVal uint32
	i := 0
	base := 10
	cutoff = (1<<32-1)/10 + 1
	maxVal = 1<<uint(32) - 1
	for ; i < len(s); i++ {
		var v byte
		d := s[i]
		switch {
		case '0' <= d && d <= '9':
			v = d - '0'
		case 'a' <= d && d <= 'z':
			v = d - 'a' + 10
		case 'A' <= d && d <= 'Z':
			v = d - 'A' + 10
		default:
			return 0, errors.New("invalid syntax")
		}
		if v >= byte(base) {
			return 0, errors.New("invalid syntax")
		}

		if n >= cutoff {
			// n*base overflows
			n = 1<<32 - 1
			return n, errors.New("value out of range")
		}
		n *= uint32(base)

		n1 := n + uint32(v)
		if n1 < n || n1 > maxVal {
			// n+v overflows
			n = 1<<32 - 1
			return n, errors.New("value out of range")
		}
		n = n1
	}
	return n, nil
}

func version() string {
	return string(content[0:intLen])
}

func totalRecord() int32 {
	return (int32(len(content)) - firstRecordOffset()) / phoneIndexLength
}

func firstRecordOffset() int32 {
	return get4(content[intLen : intLen*2])
}

// Find 二分法查询phone数据
func Find(phone string) (pr *PhoneRecord, err error) {
	pl := len(phone)
	if pl < 7 || pl > 11 {
		return nil, errors.New("illegal phone length")
	}

	var left int32
	psInt, err := getN(phone[0:7])
	if err != nil {
		return nil, errors.New("illegal phone number")
	}
	psInt32 := int32(psInt)
	right := (total - firstOffset) / phoneIndexLength
	for {
		if left > right {
			break
		}
		mid := (left + right) / 2
		offset := firstOffset + mid*phoneIndexLength
		if offset >= total {
			break
		}
		curPhone := get4(content[offset : offset+intLen])
		recordOffset := get4(content[offset+intLen : offset+intLen*2])
		cardType := content[offset+intLen*2 : offset+intLen*2+charLen][0]
		switch {
		case curPhone > psInt32:
			right = mid - 1
		case curPhone < psInt32:
			left = mid + 1
		default:
			cb := content[recordOffset:]
			endOffset := int32(bytes.Index(cb, []byte("\000")))
			data := bytes.Split(cb[:endOffset], []byte("|"))
			cardStr, ok := cardTypeMap[cardType]
			if !ok {
				cardStr = "未知电信运营商"
			}
			pr = &PhoneRecord{
				Province: string(data[0]),
				City:     string(data[1]),
				ZipCode:  string(data[2]),
				AreaZone: string(data[3]),
				CardType: cardStr,
			}
			return
		}
	}
	return nil, errors.New("phone's data not found")
}
